home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ADA Programming Guide
/
ADA Programming Guide.iso
/
adatutor
/
lrmrdr
/
chap07.doc
< prev
next >
Wrap
Text File
|
1996-01-30
|
37KB
|
887 lines
> 7. Packages
Packages are one of the four forms of program unit, of which programs can
be composed. The other forms are subprograms, task units, and generic
units.
Packages allow the specification of groups of logically related entities.
In their simplest form packages specify pools of common object and type
declarations. More generally, packages can be used to specify groups of
related entities including also subprograms that can be called from outside
the package, while their inner workings remain concealed and protected from
outside users.
References: generic unit 12, program unit 6, subprogram 6, task unit 9,
type declaration 3.3.1
> 7.1 Package Structure
A package is generally provided in two parts: a package specification and
a package body. Every package has a package specification, but not all
packages have a package body.
package_declaration ::= package_specification;
package_specification ::=
package identifier is
{basic_declarative_item}
[private
{basic_declarative_item}]
end [package_simple_name]
package_body ::=
package body package_simple_name is
[declarative_part]
[begin
sequence_of_statements
[exception
exception_handler
{exception_handler}]]
end [package_simple_name];
The simple name at the start of a package body must repeat the package
identifier. Similarly if a simple name appears at the end of the package
specification or body, it must repeat the package identifier.
If a subprogram declaration, a package declaration, a task declaration, or
a generic declaration is a declarative item of a given package
specification, then the body (if there is one) of the program unit declared
by the declarative item must itself be a declarative item of the
declarative part of the body of the given package.
Notes:
A simple form of package, specifying a pool of objects and types, does not
require a package body. One of the possible uses of the sequence of
statements of a package body is to initialize such objects. For each
subprogram declaration there must be a corresponding body (except for a
subprogram written in another language, as explained in section 13.9). If
the body of a program unit is a body stub, then a separately compiled
subunit containing the corresponding proper body is required for the
program unit (see 10.2). A body is not a basic declarative item and so
cannot appear in a package specification.
A package declaration is either a library package (see 10.2) or a
declarative item declared within another program unit.
References: basic declarative item 3.9, body stub 10.2, declarative item
3.9, declarative part 3.9, exception handler 11.2, generic body 12.2,
generic declaration 12.1, identifier 2.3, library unit 10.1, object 3.2,
package body 7.3, program unit 6, proper body 3.9, sequence of statements
5.1, simple name 4.1, subprogram body 6.3, subprogram declaration 6.1,
subunit 10.2, task body 9.1, task declaration 9.1, type 3.3
> 7.2 Package Specifications and Declarations
The first list of declarative items of a package specification is called
the visible part of the package. The optional list of declarative items
after the reserved word private is called the private part of the package.
An entity declared in the private part of a package is not visible outside
the package itself (a name denoting such an entity is only possible within
the package). In contrast, expanded names denoting entities declared in
the visible part can be used even outside the package; furthermore, direct
visibility of such entities can be achieved by means of use clauses (see
4.1.3 and 8.4).
The elaboration of a package declaration consists of the elaboration of its
basic declarative items in the given order.
Notes:
The visible part of a package contains all the information that another
program unit is able to know about the package. A package consisting of
only a package specification (that is, without a package body) can be used
to represent a group of common constants or variables, or a common pool of
objects and types, as in the examples below.
Example of a package describing a group of common variables:
package PLOTTING_DATA is
PEN_UP : BOOLEAN;
CONVERSION_FACTOR,
X_OFFSET, Y_OFFSET,
X_MIN, Y_MIN,
X_MAX, Y_MAX: REAL; -- see 3.5.7
X_VALUE : array (1 .. 500) of REAL;
Y_VALUE : array (1 .. 500) of REAL;
end PLOTTING_DATA;
Example of a package describing a common pool of objects and types:
package WORK_DATA is
type DAY is (MON, TUE, WED, THU, FRI, SAT, SUN);
type HOURS_SPENT is delta 0.25 range 0.0 .. 24.0;
type TIME_TABLE is array (DAY) of HOURS_SPENT;
WORK_HOURS : TIME_TABLE;
NORMAL_HOURS : constant TIME_TABLE :=
(MON .. THU => 8.25, FRI => 7.0, SAT | SUN => 0.0);
end WORK_DATA;
References: basic declarative item 3.9, constant 3.2.1, declarative item
3.9, direct visibility 8.3, elaboration 3.9, expanded name 4.1.3, name 4.1,
number declaration 3.2.2, object declaration 3.2.1, package 7, package
declaration 7.1, package identifier 7.1, package specification 7.1, scope
8.2, simple name 4.1, type declaration 3.3.1, use clause 8.4, variable
3.2.1
> 7.3 Package Bodies
In contrast to the entities declared in the visible part of a package
specification, the entities declared in the package body are only visible
within the package body itself. As a consequence, a package with a package
body can be used for the construction of a group of related subprograms (a
package in the usual sense), in which the logical operations available to
the users are clearly isolated from the internal entities.
For the elaboration of a package body, its declarative part is first
elaborated, and its sequence of statements (if any) is then executed. The
optional exception handlers at the end of a package body service
exceptions raised during the execution of the sequence of statements of the
package body.
Notes:
A variable declared in the body of a package is only visible within this
body and, consequently, its value can only be changed within the package
body. In the absence of local tasks, the value of such a variable remains
unchanged between calls issued from outside the package to subprograms
declared in the visible part. The properties of such a variable are
similar to those of an "own" variable of Algol 60.
The elaboration of the body of a subprogram declared in the visible part of
a package is caused by the elaboration of the body of the package. Hence a
call of such a subprogram by an outside program unit raises the exception
PROGRAM_ERROR if the call takes place before the elaboration of the package
body (see 3.9).
Example of a package:
package RATIONAL_NUMBERS is
type RATIONAL is
record
NUMERATOR : INTEGER;
DENOMINATOR : POSITIVE;
end record;
function EQUAL(X,Y : RATIONAL) return BOOLEAN;
function "/" (X,Y : INTEGER) return RATIONAL; -- to construct a rational number
function "+" (X,Y : RATIONAL) return RATIONAL;
function "-" (X,Y : RATIONAL) return RATIONAL;
function "*" (X,Y : RATIONAL) return RATIONAL;
function "/" (X,Y : RATIONAL) return RATIONAL;
end;
package body RATIONAL_NUMBERS is
procedure SAME_DENOMINATOR (X,Y : in out RATIONAL) is
begin
-- reduces X and Y to the same denominator:
...
end;
function EQUAL(X,Y : RATIONAL) return BOOLEAN is
U,V : RATIONAL;
begin
U := X;
V := Y;
SAME_DENOMINATOR (U,V);
return U.NUMERATOR = V.NUMERATOR;
end EQUAL;
function "/" (X,Y : INTEGER) return RATIONAL is
begin
if Y > 0 then
return (NUMERATOR => X, DENOMINATOR => Y);
else
return (NUMERATOR => -X, DENOMINATOR => -Y);
end if;
end "/";
function "+" (X,Y : RATIONAL) return RATIONAL is ... end "+";
function "-" (X,Y : RATIONAL) return RATIONAL is ... end "-";
function "*" (X,Y : RATIONAL) return RATIONAL is ... end "*";
function "/" (X,Y : RATIONAL) return RATIONAL is ... end "/";
end RATIONAL_NUMBERS;
References: declaration 3.1, declarative part 3.9, elaboration 3.1 3.9,
exception 11, exception handler 11.2, name 4.1, package specification 7.1,
program unit 6, program_error exception 11.1, sequence of statements 5.1,
subprogram 6, variable 3.2.1, visible part 7.2
> 7.4 Private Type and Deferred Constant Declarations
The declaration of a type as a private type in the visible part of a
package serves to separate the characteristics that can be used directly by
outside program units (that is, the logical properties) from other
characteristics whose direct use is confined to the package (the details of
the definition of the type itself). Deferred constant declarations declare
constants of private types.
private_type_declaration ::=
type identifier [discriminant_part] is [limited] private;
deferred_constant_declaration ::=
identifier_list : constant type_mark;
A private type declaration is only allowed as a declarative item of the
visible part of a package, or as the generic parameter declaration for a
generic formal type in a generic formal part.
The type mark of a deferred constant declaration must denote a private type
or a subtype of a private type; a deferred constant declaration and the
declaration of the corresponding private type must both be declarative
items of the visible part of the same package. A deferred constant
declaration with several identifiers is equivalent to a sequence of single
deferred constant declarations as explained in section 3.2.
Examples of private type declarations:
type KEY is private;
type FILE_NAME is limited private;
Example of deferred constant declaration:
NULL_KEY : constant KEY;
References: constant 3.2.1, declaration 3.1, declarative item 3.9,
deferred constant 7.4.3, discriminant part 3.7.1, generic formal part 12.1,
generic formal type 12.1, generic parameter declaration 12.1, identifier
2.3, identifier list 3.2, limited type 7.4.4, package 7, private type
7.4.1, program unit 6, subtype 3.3, type 3.3, type mark 3.3.2, visible part
7.2
> 7.4.1 Private Types
If a private type declaration is given in the visible part of a package,
then a corresponding declaration of a type with the same identifier must
appear as a declarative item of the private part of the package. The
corresponding declaration must be either a full type declaration or the
declaration of a task type. In the rest of this section explanations are
given in terms of full type declarations; the same rules apply also to
declarations of task types.
A private type declaration and the corresponding full type declaration
define a single type. The private type declaration, together with the
visible part, define the operations that are available to outside program
units (see section 7.4.2 on the operations that are available for private
types). On the other hand, the full type declaration defines other
operations whose direct use is only possible within the package itself.
If the private type declaration includes a discriminant part, the full
declaration must include a discriminant part that conforms (see 6.3.1 for
the conformance rules) and its type definition must be a record type
definition. Conversely, if the private type declaration does not include a
discriminant part, the type declared by the full type declaration (the full
type) must not be an unconstrained type with discriminants. The full type
must not be an unconstrained array type. A limited type (in particular a
task type) is allowed for the full type only if the reserved word limited
appears in the private type declaration (see 7.4.4).
Within the specification of the package that declares a private type and
before the end of the corresponding full type declaration, a restriction
applies to the use of a name that denotes the private type or a subtype of
the private type and, likewise, to the use of a name that denotes any type
or subtype that has a subcomponent of the private type. The only allowed
occurrences of such a name are in a deferred constant declaration, a type
or subtype declaration, a subprogram specification, or an entry
declaration; moreover, occurrences within derived type definitions or
within simple expressions are not allowed.
The elaboration of a private type declaration creates a private type. If
the private type declaration has a discriminant part, this elaboration
includes that of the discriminant part. The elaboration of the full type
declaration consists of the elaboration of the type definition; the
discriminant part, if any, is not elaborated (since the conforming
discriminant part of the private type declaration has already been
elaborated).
Notes:
It follows from the given rules that neither the declaration of a variable
of a private type, nor the creation by an allocator of an object of the
private type are allowed before the full declaration of the type.
Similarly before the full declaration, the name of the private type cannot
be used in a generic instantiation or in a representation clause.
References: allocator 4.8, array type 3.6, conform 6.3.1, declarative item
3.9, deferred constant declaration 7.4.3, derived type 3.4, discriminant
part 3.7.1, elaboration 3.9, entry declaration 9.5, expression 4.4, full
type declaration 3.3.1, generic instantiation 12.3, identifier 2.3,
incomplete type declaration 3.8.1, limited type 7.4.4, name 4.1, operation
3.3, package 7, package specification 7.1, private part 7.2, private type
7.4, private type declaration 7.4, record type definition 3.7,
representation clause 13.1, reserved word 2.9, subcomponent 3.3, subprogram
specification 6.1, subtype 3.3, subtype declaration 3.3.2, type 3.3, type
declaration 3.3.1, type definition 3.3.1, unconstrained array type 3.6,
variable 3.2.1, visible part 7.2
> 7.4.2 Operations of a Private Type
The operations that are implicitly declared by a private type declaration
include basic operations. These are the operations involved in assignment
(unless the reserved word limited appears in the declaration), membership
tests, selected components for the selection of any discriminant,
qualification, and explicit conversions.
For a private type T, the basic operations also include the attributes
T'BASE (see 3.3.3) and T'SIZE (see 13.7.2). For an object A of a private
type, the basic operations include the attribute A'CONSTRAINED if the
private type has discriminants (see 3.7.4), and in any case, the attributes
A'SIZE and A'ADDRESS (see 13.7.2).
Finally, the operations implicitly declared by a private type declaration
include the predefined comparison for equality and inequality unless the
reserved word limited appears in the private type declaration.
The above operations, together with subprograms that have a parameter or
result of the private type and that are declared in the visible part of the
package, are the only operations from the package that are available
outside the package for the private type.
Within the package that declares the private type, the additional
operations implicitly declared by the full type declaration are also
available. However, the redefinition of these implicitly declared
operations is allowed within the same declarative region, including between
the private type declaration and the corresponding full declaration. An
explicitly declared subprogram hides an implicitly declared operation that
has the same parameter and result type profile (this is only possible if
the implicitly declared operation is a derived subprogram or a predefined
operator).
If a composite type has subcomponents of a private type and is declared
outside the package that declares the private type, then the operations
that are implicitly declared by the declaration of the composite type
include all operations that only depend on the characteristics that result
from the private type declaration alone. (For example the operator < is
not included for a one-dimensional array type.)
If the composite type is itself declared within the package that declares
the private type (including within an inner package or generic package),
then additional operations that depend on the characteristics of the full
type are implicitly declared, as required by the rules applicable to the
composite type (for example the operator < is declared for a
one-dimensional array type if the full type is discrete). These additional
operations are implicitly declared at the earliest place within the
immediate scope of the composite type and after the full type declaration.
The same rules apply to the operations that are implicitly declared for an
access type whose designated type is a private type or a type declared by
an incomplete type declaration.
For every private type or subtype T the following attribute is defined:
T'CONSTRAINED Yields the value FALSE if T denotes an unconstrained
nonformal private type with discriminants; also yields the
value FALSE if T denotes a generic formal private type, and
the associated actual subtype is either an unconstrained type
with discriminants or an unconstrained array type; yields
the value TRUE otherwise. The value of this attribute is of
the predefined type BOOLEAN.
Note:
A private type declaration and the corresponding full type declaration
define two different views of one and the same type. Outside of the
defining package the characteristics of the type are those defined by the
visible part. Within these outside program units the type is just a
private type and any language rule that applies only to another class of
types does not apply. The fact that the full declaration might implement
the private type with a type of a particular class (for example, as an
array type) is only relevant within the package itself.
The consequences of this actual implementation are, however, valid
everywhere. For example: any default initialization of components takes
place; the attribute SIZE provides the size of the full type; task
dependence rules still apply to components that are task objects.
Example:
package KEY_MANAGER is
type KEY is private;
NULL_KEY : constant KEY;
procedure GET_KEY(K : out KEY);
function "<" (X, Y : KEY) return BOOLEAN;
private
type KEY is new NATURAL;
NULL_KEY : constant KEY := 0;
end;
package body KEY_MANAGER is
LAST_KEY : KEY := 0;
procedure GET_KEY(K : out KEY) is
begin
LAST_KEY := LAST_KEY + 1;
K := LAST_KEY;
end GET_KEY;
function "<" (X, Y : KEY) return BOOLEAN is
begin
return INTEGER(X) < INTEGER(Y);
end "<";
end KEY_MANAGER;
Notes on the example:
Outside of the package KEY_MANAGER, the operations available for objects of
type KEY include assignment, the comparison for equality or inequality, the
procedure GET_KEY and the operator "<"; they do not include other
relational operators such as ">=", or arithmetic operators.
The explicitly declared operator "<" hides the predefined operator "<"
implicitly declared by the full type declaration. Within the body of the
function, an explicit conversion of X and Y to the type INTEGER is
necessary to invoke the "<" operator of this type. Alternatively, the
result of the function could be written as not (X >= Y), since the operator
">=" is not redefined.
The value of the variable LAST_KEY, declared in the package body, remains
unchanged between calls of the procedure GET_KEY. (See also the Notes of
section 7.3.)
References: assignment 5.2, attribute 4.1.4, basic operation 3.3.3,
component 3.3, composite type 3.3, conversion 4.6, declaration 3.1,
declarative region 8.1, derived subprogram 3.4, derived type 3.4, dimension
3.6, discriminant 3.3, equality 4.5.2, full type 7.4.1, full type
declaration 3.3.1, hiding 8.3, immediate scope 8.2, implicit declaration
3.1, incomplete type declaration 3.8.1, membership test 4.5, operation 3.3,
package 7, parameter of a subprogram 6.2, predefined function 8.6,
predefined operator 4.5, private type 7.4, private type declaration 7.4,
program unit 6, qualification 4.7, relational operator 4.5, selected
component 4.1.3, subprogram 6, task dependence 9.4, visible part 7.2
> 7.4.3 Deferred Constants
If a deferred constant declaration is given in the visible part of a
package then a constant declaration (that is, an object declaration
declaring a constant object, with an explicit initialization) with the same
identifier must appear as a declarative item of the private part of the
package. This object declaration is called the full declaration of the
deferred constant. The type mark given in the full declaration must
conform to that given in the deferred constant declaration (see 6.3.1).
Multiple or single declarations are allowed for the deferred and the full
declarations, provided that the equivalent single declarations conform.
Within the specification of the package that declares a deferred constant
and before the end of the corresponding full declaration, the use of a name
that denotes the deferred constant is only allowed in the default
expression for a record component or for a formal parameter (not for a
generic formal parameter).
The elaboration of a deferred constant declaration has no other effect.
The execution of a program is erroneous if it attempts to use the value of
a deferred constant before the elaboration of the corresponding full
declaration.
Note:
The full declaration for a deferred constant that has a given private type
must not appear before the corresponding full type declaration. This is a
consequence of the rules defining the allowed uses of a name that denotes a
private type (see 7.4.1).
References: conform 6.3.1, constant declaration 3.2.1, declarative item
3.9, default expression for a discriminant 3.7.1, deferred constant 7.4,
deferred constant declaration 7.4, elaboration has no other effect 3.1,
formal parameter 6.1, generic formal parameter 12.1 12.3, identifier 2.3,
object declaration 3.2.1, package 7, package specification 7.1, private
part 7.2, record component 3.7, type mark 3.3.2, visible part 7.2
> 7.4.4 Limited Types
A limited type is a type for which neither assignment nor the predefined
comparison for equality and inequality is implicitly declared.
A private type declaration that includes the reserved word limited declares
a limited type. A task type is a limited type. A type derived from a
limited type is itself a limited type. Finally, a composite type is
limited if the type of any of its subcomponents is limited.
The operations available for a private type that is limited are as given in
section 7.4.2 for private types except for the absence of assignment and of
a predefined comparison for equality and inequality.
For a formal parameter whose type is limited and whose declaration occurs
in an explicit subprogram declaration, the mode out is only allowed if this
type is private and the subprogram declaration occurs within the visible
part of the package that declares the private type. The same holds for
formal parameters of entry declarations and of generic procedure
declarations. The corresponding full type must not be limited if the mode
out is used for any such formal parameter. Otherwise, the corresponding
full type is allowed (but not required) to be a limited type (in
particular, it is allowed to be a task type). If the full type
corresponding to a limited private type is not itself limited, then
assignment for the type is available within the package, but not outside.
The following are consequences of the rules for limited types:
- An explicit initialization is not allowed in an object declaration if
the type of the object is limited.
- A default expression is not allowed in a component declaration if the
type of the record component is limited.
- An explicit initial value is not allowed in an allocator if the
designated type is limited.
- A generic formal parameter of mode in must not be of a limited type.
Notes:
The above rules do not exclude a default expression for a formal parameter
of a limited type; they do not exclude a deferred constant of a limited
type if the full type is not limited. An explicit declaration of an
equality operator is allowed for a limited type (see 6.7).
Aggregates are not available for a limited composite type (see 3.6.2 and
3.7.4). Catenation is not available for a limited array type (see 3.6.2).
Example:
package I_O_PACKAGE is
type FILE_NAME is limited private;
procedure OPEN (F : in out FILE_NAME);
procedure CLOSE(F : in out FILE_NAME);
procedure READ (F : in FILE_NAME; ITEM : out INTEGER);
procedure WRITE(F : in FILE_NAME; ITEM : in INTEGER);
private
type FILE_NAME is
record
INTERNAL_NAME : INTEGER := 0;
end record;
end I_O_PACKAGE;
package body I_O_PACKAGE is
LIMIT : constant := 200;
type FILE_DESCRIPTOR is record ... end record;
DIRECTORY : array (1 .. LIMIT) of FILE_DESCRIPTOR;
...
procedure OPEN (F : in out FILE_NAME) is ... end;
procedure CLOSE(F : in out FILE_NAME) is ... end;
procedure READ (F : in FILE_NAME; ITEM : out INTEGER) is ... end;
procedure WRITE(F : in FILE_NAME; ITEM : in INTEGER) is ... end;
begin
...
end I_O_PACKAGE;
Notes on the example:
In the example above, an outside subprogram making use of I_O_PACKAGE may
obtain a file name by calling OPEN and later use it in calls to READ and
WRITE. Thus, outside the package, a file name obtained from OPEN acts as a
kind of password; its internal properties (such as containing a numeric
value) are not known and no other operations (such as addition or
comparison of internal names) can be performed on a file name.
This example is characteristic of any case where complete control over the
operations of a type is desired. Such packages serve a dual purpose. They
prevent a user from making use of the internal structure of the type. They
also implement the notion of an encapsulated data type where the only
operations on the type are those given in the package specification.
References: aggregate 4.3, allocator 4.8, assignment 5.2, catenation
operator 4.5, component declaration 3.7, component type 3.3, composite type
3.3, default expression for a discriminant 3.7, deferred constant 7.4.3,
derived type 3.4, designate 3.8, discriminant specification 3.7.1, equality
4.5.2, formal parameter 6.1, full type 7.4.1, full type declaration 3.3.1,
generic formal parameter 12.1 12.3, implicit declaration 3.1, initial value
3.2.1, mode 12.1.1, object 3.2, operation 3.3, package 7, predefined
operator 4.5, private type 7.4, private type declaration 7.4, record
component 3.7, record type 3.7, relational operator 4.5, subcomponent 3.3,
subprogram 6, task type 9.1 9.2, type 3.3
> 7.5 Example of a Table Management Package
The following example illustrates the use of packages in providing high
level procedures with a simple interface to the user.
The problem is to define a table management package for inserting and
retrieving items. The items are inserted into the table as they are
supplied. Each inserted item has an order number. The items are retrieved
according to their order number, where the item with the lowest order
number is retrieved first.
From the user's point of view, the package is quite simple. There is a
type called ITEM designating table items, a procedure INSERT for inserting
items, and a procedure RETRIEVE for obtaining the item with the lowest
order number. There is a special item NULL_ITEM that is returned when the
table is empty, and an exception TABLE_FULL which is raised by INSERT if
the table is already full.
A sketch of such a package is given below. Only the specification of the
package is exposed to the user.
package TABLE_MANAGER is
type ITEM is
record
ORDER_NUM : INTEGER;
ITEM_CODE : INTEGER;
QUANTITY : INTEGER;
ITEM_TYPE : CHARACTER;
end record;
NULL_ITEM : constant ITEM :=
(ORDER_NUM | ITEM_CODE | QUANTITY => 0, ITEM_TYPE => ' ');
procedure INSERT (NEW_ITEM : in ITEM);
procedure RETRIEVE(FIRST_ITEM : out ITEM);
TABLE_FULL : exception; -- raised by INSERT when table full
end;
The details of implementing such packages can be quite complex; in this
case they involve a two-way linked table of internal items. A local
housekeeping procedure EXCHANGE is used to move an internal item between
the busy and the free lists. The initial table linkages are established by
the initialization part. The package body need not be shown to the users
of the package.
package body TABLE_MANAGER is
SIZE : constant := 2000;
subtype INDEX is INTEGER range 0 .. SIZE;
type INTERNAL_ITEM is
record
CONTENT : ITEM;
SUCC : INDEX;
PRED : INDEX;
end record;
TABLE : array (INDEX) of INTERNAL_ITEM;
FIRST_BUSY_ITEM : INDEX := 0;
FIRST_FREE_ITEM : INDEX := 1;
function FREE_LIST_EMPTY return BOOLEAN is ... end;
function BUSY_LIST_EMPTY return BOOLEAN is ... end;
procedure EXCHANGE (FROM : in INDEX; TO : in INDEX) is ... end;
procedure INSERT (NEW_ITEM : in ITEM) is
begin
if FREE_LIST_EMPTY then
raise TABLE_FULL;
end if;
-- remaining code for INSERT
end INSERT;
procedure RETRIEVE (FIRST_ITEM : out ITEM) is ... end;
begin
-- initialization of the table linkages
end TABLE_MANAGER;
> 7.6 Example of a Text Handling Package
This example illustrates a simple text handling package. The users only
have access to the visible part; the implementation is hidden from them in
the private part and the package body (not shown).
From a user's point of view, a TEXT is a variable-length string. Each text
object has a maximum length, which must be given when the object is
declared, and a current value, which is a string of some length between
zero and the maximum. The maximum possible length of a text object is an
implementation-defined constant.
The package defines first the necessary types, then functions that return
some characteristics of objects of the type, then the conversion functions
between texts and the predefined CHARACTER and STRING types, and finally
some of the standard operations on varying strings. Most operations are
overloaded on strings and characters as well as on the type TEXT, in order
to minimize the number of explicit conversions the user has to write.
package TEXT_HANDLER is
MAXIMUM : constant := SOME_VALUE; -- implementation-defined
subtype INDEX is INTEGER range 0 .. MAXIMUM;
type TEXT(MAXIMUM_LENGTH : INDEX) is limited private;
function LENGTH (T : TEXT) return INDEX;
function VALUE (T : TEXT) return STRING;
function EMPTY (T : TEXT) return BOOLEAN;
function TO_TEXT (S : STRING; MAX : INDEX) return TEXT; -- maximum length MAX
function TO_TEXT (C : CHARACTER; MAX : INDEX) return TEXT;
function TO_TEXT (S : STRING) return TEXT; -- maximum length S'LENGTH
function TO_TEXT (C : CHARACTER) return TEXT;
function "&" (LEFT : TEXT; RIGHT : TEXT) return TEXT;
function "&" (LEFT : TEXT; RIGHT : STRING) return TEXT;
function "&" (LEFT : STRING; RIGHT : TEXT) return TEXT;
function "&" (LEFT : TEXT; RIGHT : CHARACTER) return TEXT;
function "&" (LEFT : CHARACTER; RIGHT : TEXT) return TEXT;
function "=" (LEFT : TEXT; RIGHT : TEXT) return BOOLEAN;
function "<" (LEFT : TEXT; RIGHT : TEXT) return BOOLEAN;
function "<=" (LEFT : TEXT; RIGHT : TEXT) return BOOLEAN;
function ">" (LEFT : TEXT; RIGHT : TEXT) return BOOLEAN;
function ">=" (LEFT : TEXT; RIGHT : TEXT) return BOOLEAN;
procedure SET (OBJECT : in out TEXT; VALUE : in TEXT);
procedure SET (OBJECT : in out TEXT; VALUE : in STRING);
procedure SET (OBJECT : in out TEXT; VALUE : in CHARACTER);
procedure APPEND (TAIL : in TEXT; TO : in out TEXT);
procedure APPEND (TAIL : in STRING; TO : in out TEXT);
procedure APPEND (TAIL : in CHARACTER; TO : in out TEXT);
procedure AMEND (OBJECT : in out TEXT; BY : in TEXT; POSITION : in INDEX);
procedure AMEND (OBJECT : in out TEXT; BY : in STRING; POSITION : in INDEX);
procedure AMEND (OBJECT : in out TEXT; BY : in CHARACTER; POSITION : in INDEX);
-- amend replaces part of the object by the given text, string, or character
-- starting at the given position in the object
function LOCATE (FRAGMENT : TEXT; WITHIN : TEXT) return INDEX;
function LOCATE (FRAGMENT : STRING; WITHIN : TEXT) return INDEX;
function LOCATE (FRAGMENT : CHARACTER; WITHIN : TEXT) return INDEX;
-- all return 0 if the fragment is not located
private
type TEXT(MAXIMUM_LENGTH : INDEX) is
record
POS : INDEX := 0;
VALUE : STRING(1 .. MAXIMUM_LENGTH);
end record;
end TEXT_HANDLER;
Example of use of the text handling package:
A program opens an output file, whose name is supplied by the string NAME.
This string has the form
[DEVICE :] [FILENAME [.EXTENSION]]
There are standard defaults for device, filename, and extension. The
user-supplied name is passed to EXPAND_FILE_NAME as a parameter, and the
result is the expanded version, with any necessary defaults added.
function EXPAND_FILE_NAME (NAME : STRING) return STRING is
use TEXT_HANDLER;
DEFAULT_DEVICE : constant STRING := "SY:";
DEFAULT_FILE_NAME : constant STRING := "RESULTS";
DEFAULT_EXTENSION : constant STRING := ".DAT";
MAXIMUM_FILE_NAME_LENGTH : constant INDEX := SOME_APPROPRIATE_VALUE;
FILE_NAME : TEXT(MAXIMUM_FILE_NAME_LENGTH);
begin
SET(FILE_NAME, NAME);
if EMPTY(FILE_NAME) then
SET(FILE_NAME, DEFAULT_FILE_NAME);
end if;
if LOCATE(':', FILE_NAME) = 0 then
SET(FILE_NAME, DEFAULT_DEVICE & FILE_NAME);
end if;
if LOCATE('.', FILE_NAME) = 0 then
APPEND(DEFAULT_EXTENSION, TO => FILE_NAME);
end if;
return VALUE(FILE_NAME);
end EXPAND_FILE_NAME;